﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using log4net;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using System.Data.SqlClient;
using System.Configuration;
using System.Data;
using EntityFramework.BulkInsert.Extensions;

namespace AzureTelemetryBlob
{
    public class BlobParser
    {
        private readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        private int recCount = 0;
        private int failureCount = 0;
        private string itemname;

        List<PageViewPerformance> listPageViewPerformance;
        List<PageResponse> listPageResponse;
        List<Request> listRequest;
        List<Exceptions> listExceptions;
        List<Message> listMessage;
        List<PerformanceCounter> listPerformanceCounter;
        List<RemoteDependency> listRemoteDependency;
        List<Audit> listAudit;

        public string StorageConnString { get; set; }
        public string FilterContainer { get; set; }
        public string FilterType { get; set; }
        public string FilterDate { get; set; }
        public string FilterTime { get; set; }
        public string FilterDateFrom { get; set; }
        public string FilterDateTo { get; set; }

        public void ParseFull()
        {
            try
            {
                CloudStorageAccount account = CloudStorageAccount.Parse(StorageConnString);

                var blobClient = account.CreateCloudBlobClient();
                var container = blobClient.GetContainerReference(FilterContainer);

                foreach (CloudBlobDirectory directory in container.ListBlobs())                             //Parent directory
                {
                    foreach (CloudBlobDirectory subDirectory in directory.ListBlobs())                      //PageViewPerformance
                    {
                        foreach (CloudBlobDirectory dir in subDirectory.ListBlobs())                        //2015-01-31
                        {
                            foreach (CloudBlobDirectory subdir in dir.ListBlobs())                          //22
                            {
                                foreach (IListBlobItem item in subdir.ListBlobs())                          //3IAwm6u3-0.blob
                                {
                                    InsertItems(container, directory, subDirectory, dir, subdir, item);

                                } //item loop
                            } //subdir loop
                        } //dir loop
                    } //subDirectory loop
                } //directory loop
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        public void ParseFullBetween()
        {
            try
            {
                CloudStorageAccount account = CloudStorageAccount.Parse(StorageConnString);

                var blobClient = account.CreateCloudBlobClient();
                var container = blobClient.GetContainerReference(FilterContainer);

                foreach (CloudBlobDirectory directory in container.ListBlobs())                             //Parent directory
                {
                    foreach (CloudBlobDirectory subDirectory in directory.ListBlobs())                      //PageViewPerformance
                    {
                        DateTime StartingDate = DateTime.Parse(FilterDateFrom);
                        DateTime EndingDate = DateTime.Parse(FilterDateTo);

                        foreach (DateTime date in GetDateRange(StartingDate, EndingDate))
                        {
                            CloudBlobDirectory dir = subDirectory.GetDirectoryReference(date.ToString("yyyy-MM-dd"));       //2015-01-31

                            foreach (CloudBlobDirectory subdir in dir.ListBlobs())                          //22
                            {
                                foreach (IListBlobItem item in subdir.ListBlobs())                          //3IAwm6u3-0.blob
                                {
                                    InsertItems(container, directory, subDirectory, dir, subdir, item);

                                } //item loop
                            } //subdir loop
                        } //date loop
                    } //subDirectory loop
                } //directory loop
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        public void ParseDeltaBetween()
        {
            try
            {
                CloudStorageAccount account = CloudStorageAccount.Parse(StorageConnString);
                var blobClient = account.CreateCloudBlobClient();
                var container = blobClient.GetContainerReference(FilterContainer);

                foreach (CloudBlobDirectory directory in container.ListBlobs())                                         //Parent directory
                {
                    CloudBlobDirectory subDirectory = directory.GetDirectoryReference(FilterType);                      //PageViewPerformance

                    DateTime StartingDate = DateTime.Parse(FilterDateFrom);
                    DateTime EndingDate = DateTime.Parse(FilterDateTo);

                    foreach (DateTime date in GetDateRange(StartingDate, EndingDate))
                    {
                        CloudBlobDirectory dir = subDirectory.GetDirectoryReference(date.ToString("yyyy-MM-dd"));       //2015-01-31

                        foreach (CloudBlobDirectory subdir in dir.ListBlobs())                                          //22
                        {
                            foreach (IListBlobItem item in subdir.ListBlobs())                                          //3IAwm6u3-0.blob
                            {
                                InsertItems(container, directory, subDirectory, dir, subdir, item);

                            } //item loop
                        } //subdir loop
                    } //date loop
                } //directory loop
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        private void InsertItems(CloudBlobContainer container, CloudBlobDirectory directory, CloudBlobDirectory subDirectory, CloudBlobDirectory dir, CloudBlobDirectory subdir, IListBlobItem item)
        {
            listPageViewPerformance = new List<PageViewPerformance>();
            listPageResponse = new List<PageResponse>();
            listRequest = new List<Request>();
            listPerformanceCounter = new List<PerformanceCounter>();
            listRemoteDependency = new List<RemoteDependency>();
            listExceptions = new List<Exceptions>();
            listMessage = new List<Message>();

            listAudit = new List<Audit>();

            FilterType = subDirectory.Uri.Segments.Last().Replace('/', ' ').Trim();
            itemname = item.Uri.ToString();

            ParseEachBlob(container, item);
            AuditBlob(container, directory, subDirectory, dir, subdir, item);

            BulkInsertDB();
        }

        public void ParseDelta()
        {
            try
            {
                CloudStorageAccount account = CloudStorageAccount.Parse(StorageConnString);
                var blobClient = account.CreateCloudBlobClient();
                var container = blobClient.GetContainerReference(FilterContainer);

                foreach (CloudBlobDirectory directory in container.ListBlobs())                         //Parent directory
                {
                    CloudBlobDirectory subDirectory = directory.GetDirectoryReference(FilterType);      //PageViewPerformance
                    CloudBlobDirectory dir = subDirectory.GetDirectoryReference(FilterDate);            //2015-01-31

                    foreach (CloudBlobDirectory subdir in dir.ListBlobs())                              //22
                    {
                        foreach (IListBlobItem item in subdir.ListBlobs())                              //3IAwm6u3-0.blob
                        {
                            InsertItems(container, directory, subDirectory, dir, subdir, item);

                        } //item loop
                    } //subdir loop
                }
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        public void ParseHourlyDelta()
        {
            try
            {
                CloudStorageAccount account = CloudStorageAccount.Parse(StorageConnString);
                var blobClient = account.CreateCloudBlobClient();
                var container = blobClient.GetContainerReference(FilterContainer);

                foreach (CloudBlobDirectory directory in container.ListBlobs())                         //Parent directory
                {
                    CloudBlobDirectory subDirectory = directory.GetDirectoryReference(FilterType);      //PageViewPerformance
                    CloudBlobDirectory dir = subDirectory.GetDirectoryReference(FilterDate);            //2015-01-31
                    CloudBlobDirectory subdir = dir.GetDirectoryReference(FilterTime);                  //22

                    foreach (IListBlobItem item in subdir.ListBlobs())                                  //3IAwm6u3-0.blob
                    {
                        InsertItems(container, directory, subDirectory, dir, subdir, item);

                    } //item loop
                }
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        public void ParseHourwise()
        {
            try
            {
                CloudStorageAccount account = CloudStorageAccount.Parse(StorageConnString);

                var blobClient = account.CreateCloudBlobClient();
                var container = blobClient.GetContainerReference(FilterContainer);

                foreach (CloudBlobDirectory directory in container.ListBlobs())                             //Parent directory
                {
                    foreach (CloudBlobDirectory subDirectory in directory.ListBlobs())                      //PageViewPerformance
                    {
                        CloudBlobDirectory dir = subDirectory.GetDirectoryReference(FilterDate);            //2015-01-31
                        CloudBlobDirectory subdir = dir.GetDirectoryReference(FilterTime);                  //22

                        foreach (IListBlobItem item in subdir.ListBlobs())                                  //3IAwm6u3-0.blob
                        {
                            InsertItems(container, directory, subDirectory, dir, subdir, item);

                        } //item loop
                    } //subdir loop
                } //directory loop
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        public void ParseDatewise()
        {
            try
            {
                CloudStorageAccount account = CloudStorageAccount.Parse(StorageConnString);

                var blobClient = account.CreateCloudBlobClient();
                var container = blobClient.GetContainerReference(FilterContainer);

                foreach (CloudBlobDirectory directory in container.ListBlobs())                             //Parent directory
                {
                    foreach (CloudBlobDirectory subDirectory in directory.ListBlobs())                      //PageViewPerformance
                    {
                        CloudBlobDirectory dir = subDirectory.GetDirectoryReference(FilterDate);            //2015-01-31

                        foreach (CloudBlobDirectory subdir in dir.ListBlobs())                              //22
                        {
                            foreach (IListBlobItem item in subdir.ListBlobs())                              //3IAwm6u3-0.blob
                            {
                                InsertItems(container, directory, subDirectory, dir, subdir, item);

                            } //item loop
                        } //subdir loop
                    } //subDirectory loop
                } //directory loop
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        public void ParseEventtypewise()
        {
            try
            {
                CloudStorageAccount account = CloudStorageAccount.Parse(StorageConnString);

                var blobClient = account.CreateCloudBlobClient();
                var container = blobClient.GetContainerReference(FilterContainer);

                foreach (CloudBlobDirectory directory in container.ListBlobs())                     //Parent directory
                {
                    CloudBlobDirectory subDirectory = directory.GetDirectoryReference(FilterType);  //PageViewPerformance

                    foreach (CloudBlobDirectory dir in subDirectory.ListBlobs())                    //2015-01-31
                    {
                        foreach (CloudBlobDirectory subdir in dir.ListBlobs())                      //22
                        {
                            foreach (IListBlobItem item in subdir.ListBlobs())                      //3IAwm6u3-0.blob
                            {
                                InsertItems(container, directory, subDirectory, dir, subdir, item);

                            } //item loop
                        } //subdir loop
                    } //dir loop
                } //directory loop
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        private void ParseEachBlob(CloudBlobContainer container, IListBlobItem item)
        {
            try
            {
                var blob = container.GetBlockBlobReference(item.Parent.Prefix + item.Uri.Segments.Last());

                string json;

                using (var memoryStream = new MemoryStream())
                {
                    blob.DownloadToStream(memoryStream);
                    json = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray());

                    IEnumerable<string> entities = json.Split('\n').Where(s => !string.IsNullOrWhiteSpace(s));

                    recCount = entities.Count();
                    failureCount = 0; //resetting failure count

                    #region "Traverse each JSON document & dump in DB"

                    ParseEachJSONDocument(item, entities);

                    #endregion
                }
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        private void ParseEachJSONDocument(IListBlobItem item, IEnumerable<string> entities)
        {
            foreach (var entity in entities)
            {
                var reader = new JsonFx.Json.JsonReader();
                dynamic output = reader.Read(entity);

                Dictionary<string, object> dict = new Dictionary<string, object>();

                GenerateDictionary((System.Dynamic.ExpandoObject)output, dict, "");

                switch (FilterType)
                {
                    case "PageViewPerformance":

                        #region PageViewPerformance

                        if (dict.ContainsKey("clientPerformance"))
                            GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["clientPerformance"])[0], dict, "");

                        if (dict.ContainsKey("context_custom_dimensions"))
                            if (dict["context_custom_dimensions"].GetType() == typeof(System.Dynamic.ExpandoObject[]))
                                GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["context_custom_dimensions"])[0], dict, "");

                        PageViewPerformance objPageViewPerformance = (PageViewPerformance)GetObject(dict);
                        listPageViewPerformance.Add(objPageViewPerformance);

                        break;

                        #endregion

                    case "PageViews":

                        #region PageViews

                        if (dict.ContainsKey("view"))
                            GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["view"])[0], dict, "");

                        if (dict.ContainsKey("context_custom_dimensions"))
                            if (dict["context_custom_dimensions"].GetType() == typeof(System.Dynamic.ExpandoObject[]))
                                GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["context_custom_dimensions"])[0], dict, "");

                        PageResponse objPageResponse = (PageResponse)GetObject(dict);
                        listPageResponse.Add(objPageResponse);

                        break;

                        #endregion

                    case "Requests":

                        #region Requests

                        if (dict.ContainsKey("request"))
                            GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["request"])[0], dict, "");

                        if (dict.ContainsKey("context_custom_dimensions"))
                            if (dict["context_custom_dimensions"].GetType() == typeof(System.Dynamic.ExpandoObject[]))
                                GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["context_custom_dimensions"])[0], dict, "");

                        Request objRequest = (Request)GetObject(dict);
                        listRequest.Add(objRequest);

                        break;

                        #endregion

                    case "Exceptions":

                        #region Exceptions

                        if (dict.ContainsKey("basicException"))
                            GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["basicException"])[0], dict, "");

                        if (dict.ContainsKey("context_custom_dimensions"))
                            if (dict["context_custom_dimensions"].GetType() == typeof(System.Dynamic.ExpandoObject[]))
                                GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["context_custom_dimensions"])[0], dict, "");

                        Exceptions objExceptions = (Exceptions)GetObject(dict);
                        listExceptions.Add(objExceptions);

                        break;

                        #endregion

                    case "Messages":

                        #region Messages

                        if (dict.ContainsKey("message"))
                            GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["message"])[0], dict, "");

                        Message objMessage = (Message)GetObject(dict);
                        listMessage.Add(objMessage);

                        break;

                        #endregion

                    case "PerformanceCounters":

                        #region PerformanceCounter

                        if (dict.ContainsKey("performanceCounter"))
                            GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["performanceCounter"])[0], dict, "");

                        PerformanceCounter objPerformanceCounter = (PerformanceCounter)GetObject(dict);
                        listPerformanceCounter.Add(objPerformanceCounter);

                        break;

                        #endregion

                    case "Rdd":

                        #region RemoteDependency

                        if (dict.ContainsKey("remoteDependency"))
                            GenerateDictionary(((System.Dynamic.ExpandoObject[])dict["remoteDependency"])[0], dict, "");

                        RemoteDependency objRemoteDependency = (RemoteDependency)GetObject(dict);
                        listRemoteDependency.Add(objRemoteDependency);

                        break;

                        #endregion

                    default:
                        break;
                }
            }
        }

        private void BulkInsertDB()
        {
            try
            {
                using (var db = new TelemetryContext())
                {
                    db.Configuration.AutoDetectChangesEnabled = false;
                    db.Configuration.ValidateOnSaveEnabled = false;

                    db.Database.CommandTimeout = 300;

                    switch (FilterType)
                    {
                        case "PageViewPerformance":
                            db.BulkInsert(listPageViewPerformance);
                            break;

                        case "PageViews":
                            db.BulkInsert(listPageResponse);
                            break;

                        case "Requests":
                            db.BulkInsert(listRequest);
                            break;

                        case "Exceptions":
                            db.BulkInsert(listExceptions);
                            break;

                        case "Messages":
                            db.BulkInsert(listMessage);
                            break;

                        case "PerformanceCounters":
                            db.BulkInsert(listPerformanceCounter);
                            break;

                        case "Rdd":
                            db.BulkInsert(listRemoteDependency);
                            break;

                        default:
                            break;
                    }

                    db.BulkInsert(listAudit);

                    db.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                //failureCount++;
                WritetoLog(itemname, ex);
            }
        }

        private void GenerateDictionary(System.Dynamic.ExpandoObject output, Dictionary<string, object> dict, string parent)
        {
            try
            {
                foreach (var v in output)
                {
                    string key = parent + v.Key;
                    object o = v.Value;

                    if (o.GetType() == typeof(System.Dynamic.ExpandoObject))
                    {
                        GenerateDictionary((System.Dynamic.ExpandoObject)o, dict, key + "_");
                    }
                    else
                    {
                        if (!dict.ContainsKey(key))
                        {
                            dict.Add(key, o);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        public object GetObject(IDictionary<string, object> d)
        {
            PropertyInfo[] props = null;
            object res = null;

            try
            {
                switch (FilterType)
                {
                    case "PageViewPerformance":

                        #region PageViewPerformance

                        props = typeof(PageViewPerformance).GetProperties();
                        res = Activator.CreateInstance<PageViewPerformance>();
                        break;

                        #endregion

                    case "PageViews":

                        #region PageViews

                        props = typeof(PageResponse).GetProperties();
                        res = Activator.CreateInstance<PageResponse>();
                        break;

                        #endregion

                    case "Requests":

                        #region Requests

                        props = typeof(Request).GetProperties();
                        res = Activator.CreateInstance<Request>();
                        break;

                        #endregion

                    case "Exceptions":

                        #region Exceptions

                        props = typeof(Exceptions).GetProperties();
                        res = Activator.CreateInstance<Exceptions>();
                        break;

                        #endregion

                    case "Messages":

                        #region Messages

                        props = typeof(Message).GetProperties();
                        res = Activator.CreateInstance<Message>();
                        break;

                        #endregion

                    case "PerformanceCounters":

                        #region PerformanceCounters

                        props = typeof(PerformanceCounter).GetProperties();
                        res = Activator.CreateInstance<PerformanceCounter>();
                        break;

                        #endregion

                    case "Rdd":

                        #region PerformanceCounters

                        props = typeof(RemoteDependency).GetProperties();
                        res = Activator.CreateInstance<RemoteDependency>();
                        break;

                        #endregion

                    default:
                        break;
                }

                for (int i = 0; i < props.Length; i++)
                {
                    if (props[i].CanWrite && d.ContainsKey(props[i].Name))
                    {
                        props[i].SetValue(res, d[props[i].Name], null);
                    }
                }
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }

            return res;
        }

        private void AuditBlob(CloudBlobContainer container, CloudBlobDirectory directory, CloudBlobDirectory subDirectory, CloudBlobDirectory dir, CloudBlobDirectory subdir, IListBlobItem item)
        {
            try
            {
                Audit objAudit = new Audit();
                objAudit.ContainerName = container.Name.Trim();
                objAudit.Directory = directory.Prefix.Replace('/', ' ').Trim();
                objAudit.SubDirectory = subDirectory.Uri.Segments.Last().Replace('/', ' ').Trim();
                objAudit.DateDirectory = dir.Uri.Segments.Last().Replace('/', ' ').Trim();
                objAudit.FileDirectory = subdir.Uri.Segments.Last().Replace('/', ' ').Trim();
                objAudit.FileName = item.Uri.Segments.Last().Replace('/', ' ').Trim();
                objAudit.RecordCount = recCount;
                objAudit.FailureCount = failureCount;

                listAudit.Add(objAudit);
            }
            catch (Exception ex)
            {
                WritetoLog(itemname, ex);
            }
        }

        private void WritetoLog(string searchstring, Exception ex)
        {
            if (ex.InnerException != null)
            {
                if (ex.InnerException.InnerException != null)
                {
                    log.Error(searchstring + "\n" + ex.InnerException.InnerException.ToString());
                }
                else
                {
                    log.Error(searchstring + "\n" + ex.InnerException.ToString());
                }
            }
            else
            {
                log.Error(searchstring + "\n" + ex.ToString());
            }
        }

        private List<DateTime> GetDateRange(DateTime StartingDate, DateTime EndingDate)
        {
            if (StartingDate > EndingDate)
            {
                return null;
            }

            List<DateTime> rv = new List<DateTime>();
            DateTime tmpDate = StartingDate;

            do
            {
                rv.Add(tmpDate);
                tmpDate = tmpDate.AddDays(1);
            } while (tmpDate <= EndingDate);

            return rv;
        }

        

    }

}


